module Multi_Control_Unit(
  iClk,
  iReset,
  iInstWrENB,
	iOPcode,
	iFuncCode,
	PCWriteCond, 
	PCWrite, 
	lorD,
	MemRead, 
	MemWrite, 
	MemtoReg, 
	IRWrite,
	PCSource,
	ALUSrcA, 
  ALUSrcB,
	RegWrite, 
	RegDst,
	ALUCont
	);
	input [5:0] iOPcode, iFuncCode;
	input iClk, iReset, iInstWrENB;
	output PCWriteCond, PCWrite, lorD, MemRead, MemWrite, MemtoReg, IRWrite, ALUSrcA, RegWrite, RegDst;
	output [1:0] ALUSrcB, PCSource;
	output [2:0] ALUCont;
	
	/* implementation of circuit on page 54 of The Processor: Datapath and Control */
	wire w_R_format, w_ori, w_lw, w_sw, w_beq, w_jump;	
	
	assign w_R_format = (iOPcode == 6'b000000);
	assign w_ori = (iOPcode == 6'b001101);
	assign w_lw = (iOPcode == 6'b100011);
	assign w_sw = (iOPcode == 6'b101011);
	assign w_beq = (iOPcode == 6'b000100);
	assign w_jump = ~(w_R_format | w_ori | w_lw | w_sw | w_beq);
	
	
	

	
	// Finite State Machine //
	reg [3:0]wState;
	reg PCWriteCond, PCWrite, lorD, MemRead, MemWrite, MemtoReg, IRWrite, ALUSrcA, RegWrite, RegDst;
	reg [1:0] ALUSrcB, ALUOp, PCSource;
	reg [2:0] ALUCont;
	
	always @(posedge iClk)
		begin
			if(iReset)
				begin
					wState <= 4'b0000;
				end
			else if(~iInstWrENB)
				begin
					case(wState)
					  4'b0000:begin
					    MemRead <= 1;
					    ALUSrcA <= 0;
					    lorD <= 0;
					    IRWrite <= 1;
					    ALUSrcB <= 2'b01;
					    ALUOp <= 2'b00;
					    PCWrite <= 1;
					    PCSource <= 2'b00;
					    PCWriteCond <= 0;
					    MemWrite <= 0;
					    MemtoReg <= 0;
					    RegWrite <= 0;
					    RegDst <= 0;
					    wState <= 4'b0001;
					   end
					   
					   4'b0001:begin
					     ALUSrcA <= 0;
					     ALUSrcB <= 2'b11;
					     ALUOp <= 2'b00;
					     if(w_lw || w_sw || w_ori)
					       begin
					         wState <= 4'b0010;
					       end
					     else if(w_R_format)
					       begin
					         wState <= 4'b0110;
					       end
					     else if(w_beq)
					       begin
					         wState <= 4'b1000;
					       end
					     else if(w_jump)
					       begin
					         wState <= 4'b1001;
					       end
					   end
					   4'b0010:begin//2
					     ALUSrcA <= 1;
					     ALUSrcB <= 2'b10;
					     ALUOp <= 2'b00;
					     if(w_lw)
					       begin
					         wState <= 4'b0011;
					       end
					     else if(w_sw)
					       begin
					         wState <= 4'b0101;
					       end
					     else if(w_ori)
					       begin
					         wState <= 4'b0111;
					       end
					   end
					   4'b0011:begin//3
					     MemRead <= 1;
					     lorD <= 1;
					     wState <= 4'b0100;
					   end
					   4'b0100:begin//4
					     RegDst <= 0;
					     RegWrite <= 1;
					     MemtoReg <= 1;
					     wState <= 4'b0000;
					   end
					   4'b0101:begin//5
					     MemWrite <= 1;
					     lorD <= 1;
					     wState <= 4'b0000;
					   end
					   4'b0110:begin//6
					     ALUSrcA <= 1;
					     ALUSrcB <= 2'b00;
					     ALUOp <= 2'b10;
					     wState <= 4'b0111;
					   end
					   4'b0111:begin//7
					     RegDst <= 1;
					     RegWrite <= 1;
					     MemtoReg <= 0;
					     wState <= 4'b0000;
					   end
					   4'b1000:begin//8
					     ALUSrcA <= 1;
					     ALUSrcB <= 2'b00;
					     ALUOp <= 2'b01;
					     PCWriteCond <= 1;
					     PCSource <= 2'b01;
					     wState <= 4'b0000;
					   end
					   4'b1001:begin//9
					     PCWrite <= 1;
					     PCSource <= 2'b10;
					     wState <= 4'b0000;
					   end

					endcase					
				end
				else begin
				end
				
					/* ALU control signal */	
	 ALUCont[2] <= (iReset || iInstWrENB || PCWrite)? 0:(~w_ori & ( ALUOp[0] | (ALUOp[1] & iFuncCode[1]) )) ;
	 ALUCont[1] <= (iReset || iInstWrENB || PCWrite)? 1:(~w_ori & ( ~ALUOp[1] | ~iFuncCode[2] )) ;
	 ALUCont[0] <= (iReset || iInstWrENB || PCWrite)? 0:(w_ori | ( ALUOp[1] & ( iFuncCode[3] | iFuncCode[0]) )) ;
		end
	
	
	
	
	
	
	
endmodule
